package com.lyz.redis.service;

import org.springframework.data.redis.connection.RedisZSetCommands;
import org.springframework.data.redis.core.Cursor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ScanOptions;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.util.ObjectUtils;

import java.util.Collection;
import java.util.List;
import java.util.Set;

public class SortSetRedisService<V> extends RedisService<String,V>{

    public SortSetRedisService(RedisTemplate<String, V> redisTemplate) {
        super(redisTemplate);
    }

    /**
     * 向有序集合添加一个成员，或者更新已存在成员的分数
     * @param k key
     * @param v value
     * @param score 排序分数
     * @return 设置成功否
     */
    public Boolean set(String k,V v,Double score){
        return this.redisTemplate.opsForZSet().add(k,v,score);
    }

    /**
     * 向有序集合添加多个成员，或者更新已存在成员的分数
     * @param k key
     * @param tuples 成员和分数集合
     * @return 设置成功的成员数
     */
    public Long multiSet(String k, Set<ZSetOperations.TypedTuple<V>> tuples){
        return this.redisTemplate.opsForZSet().add(k, tuples);
    }

    /**
     * 获取有序集合的成员数
     * @param k redisKey
     * @return 成员数量
     */
    public Long size(String k){
        return this.redisTemplate.opsForZSet().size(k);
    }

    /**
     * 有序集合中对指定成员的分数加上增量 increment
     * @param k key
     * @param v value
     * @param delta delta
     * @return double
     */
    public Double increment(String k,V v,Double delta){
        return this.redisTemplate.opsForZSet().incrementScore(k,v,delta);
    }

    /**
     * 获取交集并存到新的key中
     * @param k1 key集合1
     * @param k2 set集合2
     * @param k3 交集
     */
    public Long intersectionAndStore(String k1,String k2,String k3){
        return this.redisTemplate.opsForZSet().intersectAndStore(k1, k2, k3);
    }

    /**
     * 在有序集合中计算指定字典区间内成员数量
     * @param k key
     * @param start 起始位置
     * @param end 结束位置
     * @return 成员数量
     */
    public Long count(String k,Double start,Double end){
        return this.redisTemplate.opsForZSet().count(k,start,end);
    }

    /**
     * 通过值字典区间返回成员数
     * 值集合只能时字母开头的字符串，非全叫符号以及汉字，否则无法检索
     * @param k key
     * @param range 区间
     * @return 成员数量
     */
    public Long lexCount(String k, RedisZSetCommands.Range range){
        RedisZSetCommands.Range.Boundary max = range.getMax();
        return this.redisTemplate.opsForZSet().lexCount(k,range);
    }

    /**
     * 通过值字典区间返回有序集合指定区间内的成员
     * 值集合只能时字母开头的字符串，非全叫符号以及汉字，否则无法检索
     * @param k key
     * @param start 起始位置
     * @param end 结束位置
     * @return value 集合
     */
    public Set<V> rangeByIndex(String k, Long start, Long end){
        return this.redisTemplate.opsForZSet().range(k,start,end);
    }


    /**
     * 通过索引区间返回有序集合的成员
     * @param k redis key
     * @param start 索引开始位置
     * @param end 索引结束位置
     * @return V set集合
     */
    public Set<V> rangeByScore(String k,Long start,Long end){
        return this.redisTemplate.opsForZSet().rangeByScore(k,start,end);
    }

    /**
     * 通过索引区间返回有序集合的成员，控制取值起始位置和取值数量
     * @param k redis key
     * @param start 索引开始位置
     * @param end 索引结束位置
     * @param limit 通过 offset控制范围内开始位置，通过count控制取值数量
     * @return V set集合
     */
    public Set<V> rangeByScore(String k, Long start, Long end, RedisZSetCommands.Limit limit){
        return this.redisTemplate.opsForZSet().rangeByScore(k,start,end,limit.getOffset(),limit.getCount());
    }

    /**
     * 通过字典区间返回有序集合的成员
     * @param k key
     * @param max 最大分数
     * @param min 最小分数
     * @return set v集合
     */
    public Set<V> rangeByScore(String k,Double min, Double max){
        return this.redisTemplate.opsForZSet().rangeByScore(k,min,max);
    }

    /**
     * 通过索引区间返回有序集合的成员
     * 值集合只能时字母开头的字符串，非全叫符号以及汉字，否则无法检索
     * @param k key
     * @param range 字典区间
     * @param limit 取值开始位置，取值数量
     * @return set v集合
     */
    public Set<V> rangeByLex(String k,RedisZSetCommands.Range range,RedisZSetCommands.Limit limit){
        return this.redisTemplate.opsForZSet().rangeByLex(k,range,limit);
    }

    /**
     * 返回有序集合中指定成员的索引
     * @param k key
     * @param v value
     * @return index
     */
    public Long index(String k,V v){
        return this.redisTemplate.opsForZSet().rank(k,v);
    }

    /**
     * 移除有序集合中的一个或多个成员
     * @param k key
     * @param vs k
     * @return ""
     */
    @SafeVarargs
    public final Long remove(String k, V... vs){
        return this.redisTemplate.opsForZSet().remove(k, (Object[]) vs);
    }

    /**
     * 移除有序集合中给定的字典区间的所有成员
     * 值集合只能时字母开头的字符串，非全叫符号以及汉字，否则无法检索
     * @param k key
     * @param start 索引开始位置
     * @param end 索引结束位置
     * @return long
     */
    public Long removeRangeByIndex(String k, Long start,Long end){
        return this.redisTemplate.opsForZSet().removeRange(k,start,end);
    }

    /**
     * 移除有序集合中给定的排名区间的所有成员
     * @param k key
     * @param range 排名区间？
     * @return 移除成员数量
     */
    public Long removeRangeByLex(String k,RedisZSetCommands.Range range){
        return this.redisTemplate.opsForZSet().removeRangeByLex(k,range);
    }

    /**
     * 移除有序集合中给定的分数区间的所有成员
     * @param k key
     * @param min 最小分数
     * @param max 最大分数
     * @return 移除成员数量
     */
    public Long removeRangeByScore(String k,Double min,Double max){
        return this.redisTemplate.opsForZSet().removeRangeByScore(k,min,max);
    }

    /**
     * 返回有序集中指定区间内的成员，通过索引，分数从高到低
     * @param k
     * @param start
     * @param end
     * @return
     */
    public Set<ZSetOperations.TypedTuple<V>> rangeWithScoresByIndex(String k, Long start, Long end){
        return this.redisTemplate.opsForZSet().rangeWithScores(k,start,end);
    }

    /**
     * 返回有序集中指定分数区间内的成员，分数从高到低排序
     * @param k
     * @param min
     * @param max
     * @return
     */
    public Set<ZSetOperations.TypedTuple<V>> rangeWithScoresByScore(String k, Double min, Double max){
        return this.redisTemplate.opsForZSet().rangeByScoreWithScores(k,min,max);
    }

    /**
     * 返回有序集合中指定成员的排名，有序集成员按分数值递减(从大到小)排序
     * @param k redis key
     * @param min 分数值下限
     * @param max 分数值上限
     * @param offset 偏移量
     * @param count 返回数据条数
     * @return 有序集合
     */
    public Set<ZSetOperations.TypedTuple<V>> reverseRangeByScoreWithScores(String k, Double min, Double max,Long offset,Long count){
        return this.redisTemplate.opsForZSet().reverseRangeByScoreWithScores(k, min, max,offset,count);
    }

    /**
     * 获取分数范围类的有序集合
     * @param k redisKey
     * @param min 分数值下限
     * @param max 分数值上限
     * @return 有序集合
     */
    public Set<ZSetOperations.TypedTuple<V>> reverseRangeByScoreWithScores(String k, Double min, Double max){
        return this.redisTemplate.opsForZSet().reverseRangeByScoreWithScores(k, min, max);
    }

    /**
     * 返回有序集中，成员的分数值
     * @param k key
     * @param v value数组
     * @return 分数集合
     */
    @SafeVarargs
    public final List<Double> score(String k, V... v){
        return this.redisTemplate.opsForZSet().score(k, (Object[]) v);
    }

    /**
     * 获取多个KEY集合的并集，存储到新的KEY中，相同的value 分数相加
     * 计算给定的一个或多个有序集的并集，并存储在新的 key 中
     * @param k key
     * @param ks key 集合
     * @param newKey 并集的key
     * @return 并集的value 数量
     */
    public Long unionAndStore(String k, Collection<String> ks,String newKey){
        return this.redisTemplate.opsForZSet().unionAndStore(k,ks,newKey);
    }


    /**
     * 迭代有序集合中的元素（包括元素成员和元素分值）
     * @param k redisKey
     * @param handler 处理器
     * @param count 迭代数量，默认 1000
     * @param pattern 正则，默认 ”*“
     */
    public void scan(String k,ScanHandler<V> handler,Long count,String pattern){
        ScanOptions options = ScanOptions.scanOptions()
                .count(ObjectUtils.isEmpty(count)?1000:count)
                .match(ObjectUtils.isEmpty(pattern)?"*":pattern).build();
        Cursor<ZSetOperations.TypedTuple<V>> cursor = this.redisTemplate.opsForZSet().scan(k, options);
        while (cursor.hasNext()){
            ZSetOperations.TypedTuple<V> next = cursor.next();
            Double score = next.getScore();
            V value = next.getValue();
            handler.handle(value,score);
        }
        cursor.close();
    }

    public interface ScanHandler<V>{
       void handle(V v,Double score);
    }
}
